#include <Precomp.h>
#include <UserInput.h>
#include <GameWorld.h>
#include <ServerConnection.h>
#include <InputManager.h>
#include <InputListener.h>
#include <Player.h>


CUserInput::CUserInput(CServerConnection* pServerConnection, CGameWorld* pGameWorld, CInputManager* pInputManager) : m_pServerConnection(pServerConnection), 
                                                                                                                     m_pGameWorld(pGameWorld),
                                                                                                                     m_pInputManager(pInputManager)
{

}

enum EKeyboardModifier
{
    eKeyboardModifier_None,
    eKeyboardModifier_Shift,
    eKeyboardModifier_Alt,
    eKeyboardModifier_Ctrl
};

struct MouseButtonToActionMapping
{
    EMouseButtonID eMouseButtonID;
    EKeyboardModifier eKeyboardModifiers;
    EUserAction eUserAction;
};

const static MouseButtonToActionMapping c_rgMouseMappings[] =
{
    { eMouseButtonID_0, eKeyboardModifier_None, eUserAction_FirePhoton },
    { eMouseButtonID_0, eKeyboardModifier_Shift, eUserAction_GetPlanetDetail },

    { eMouseButtonID_1, eKeyboardModifier_None, eUserAction_SetCourse },
    { eMouseButtonID_1, eKeyboardModifier_Shift, eUserAction_ToggleTractor },
    { eMouseButtonID_2, eKeyboardModifier_None, eUserAction_FirePhaser },
    { eMouseButtonID_2, eKeyboardModifier_Shift, eUserAction_TogglePressor },
};

const static int c_cMouseMappings = sizeof(c_rgMouseMappings) / sizeof(c_rgMouseMappings[0]);

void CUserInput::MousePressed(EMouseButtonID eMouseButtonID, const CUserInputMouseHitData& mouseHitData)
{
    for(int i = 0; i < c_cMouseMappings; i++)
    {
        if(c_rgMouseMappings[i].eMouseButtonID == eMouseButtonID && IsModifierActive(c_rgMouseMappings[i].eKeyboardModifiers))
        {
            ExecuteAction(c_rgMouseMappings[i].eUserAction, mouseHitData);
        }
    }
}

struct KeyboardButtonToActionMapping
{
    EKeyboardButtonID eKeyboardButtonID;
    EKeyboardModifier eKeyboardModifiers;
    EUserAction eUserAction;
};

const static KeyboardButtonToActionMapping c_rgKeyboardMappings[] =
{
    { eKeyboardButtonID_0, eKeyboardModifier_None, eUserAction_SetSpeed0 },
    { eKeyboardButtonID_1, eKeyboardModifier_None, eUserAction_SetSpeed1 },
    { eKeyboardButtonID_2, eKeyboardModifier_None, eUserAction_SetSpeed2 },
    { eKeyboardButtonID_3, eKeyboardModifier_None, eUserAction_SetSpeed3 },
    { eKeyboardButtonID_4, eKeyboardModifier_None, eUserAction_SetSpeed4 },
    { eKeyboardButtonID_5, eKeyboardModifier_None, eUserAction_SetSpeed5 },
    { eKeyboardButtonID_6, eKeyboardModifier_None, eUserAction_SetSpeed6 },
    { eKeyboardButtonID_7, eKeyboardModifier_None, eUserAction_SetSpeed7 },
    { eKeyboardButtonID_8, eKeyboardModifier_None, eUserAction_SetSpeed8 },
    { eKeyboardButtonID_9, eKeyboardModifier_None, eUserAction_SetSpeed9 },

    { eKeyboardButtonID_0, eKeyboardModifier_Shift, eUserAction_SetSpeed10 },
    { eKeyboardButtonID_1, eKeyboardModifier_Shift, eUserAction_SetSpeed11 },
    { eKeyboardButtonID_2, eKeyboardModifier_Shift, eUserAction_SetSpeed12 },


    { eKeyboardButtonID_B, eKeyboardModifier_None, eUserAction_BombPlanet },
    { eKeyboardButtonID_C, eKeyboardModifier_None, eUserAction_ToggleCloak },
    { eKeyboardButtonID_D, eKeyboardModifier_None, eUserAction_DetEnemyTorps },
    { eKeyboardButtonID_E, eKeyboardModifier_None, eUserAction_ToggleDockingPermission },
    { eKeyboardButtonID_F, eKeyboardModifier_None, eUserAction_FirePlasma },
    { eKeyboardButtonID_L, eKeyboardModifier_None, eUserAction_LockOn },
    { eKeyboardButtonID_O, eKeyboardModifier_None, eUserAction_Orbit },
    { eKeyboardButtonID_P, eKeyboardModifier_None, eUserAction_FirePhaser },
    { eKeyboardButtonID_R, eKeyboardModifier_None, eUserAction_EnterRepairMode },
    { eKeyboardButtonID_S, eKeyboardModifier_None, eUserAction_ToggleShields },
    { eKeyboardButtonID_T, eKeyboardModifier_None, eUserAction_ToggleTractor },
    { eKeyboardButtonID_X, eKeyboardModifier_None, eUserAction_BeamDownArmies },
    { eKeyboardButtonID_Z, eKeyboardModifier_None, eUserAction_BeamUpArmies },
    { eKeyboardButtonID_Multiply, eKeyboardModifier_None, eUserAction_PracticeRobot },


    { eKeyboardButtonID_D, eKeyboardModifier_Shift, eUserAction_DetMyTorps },
    { eKeyboardButtonID_T, eKeyboardModifier_Shift, eUserAction_TogglePressor },

};

const static int c_cKeyboardMappings = sizeof(c_rgKeyboardMappings) / sizeof(c_rgKeyboardMappings[0]);


bool CUserInput::IsModifierActive(EKeyboardModifier eKeyboardModifier)
{
    bool isShiftDown = !!GetAsyncKeyState(VK_SHIFT);
    bool isCtrlDown = !!GetAsyncKeyState(VK_CONTROL);
    bool isAltDown = !!GetAsyncKeyState(VK_MENU);

    if(eKeyboardModifier == eKeyboardModifier_None && !isShiftDown && !isCtrlDown && !isAltDown)
    {
        return true;
    }

    if(eKeyboardModifier == eKeyboardModifier_Ctrl && !isShiftDown && isCtrlDown && !isAltDown)
    {
        return true;
    }

    if(eKeyboardModifier == eKeyboardModifier_Alt && !isShiftDown && !isCtrlDown && isAltDown)
    {
        return true;
    }

    if(eKeyboardModifier == eKeyboardModifier_Shift && isShiftDown && !isCtrlDown && !isAltDown)
    {
        return true;
    }

    return false;
}

void CUserInput::KeyPressed(EKeyboardButtonID eKeyboardButtonID, const CUserInputMouseHitData& mouseHitData)
{
    for(int i = 0; i < c_cKeyboardMappings; i++)
    {
        if(c_rgKeyboardMappings[i].eKeyboardButtonID == eKeyboardButtonID && IsModifierActive(c_rgKeyboardMappings[i].eKeyboardModifiers))
        {
            ExecuteAction(c_rgKeyboardMappings[i].eUserAction, mouseHitData);
        }
    }
}

void CUserInput::ExecuteSpeedAction(char chNewSpeed)
{
    struct speed_cpacket packet;

    packet.type = (char) EClientPacketType_SetSpeed;
    packet.speed = (char)chNewSpeed;

    m_pServerConnection->SendPacket((packet_base*) &packet);

}



void CUserInput::ExecuteAction(EUserAction eUserAction, const CUserInputMouseHitData& mouseHitData)
{
    DWORD dwXLocation = mouseHitData.GetXLocation();
    DWORD dwYLocation = mouseHitData.GetYLocation();
    BYTE bAngle = mouseHitData.GetAngle();

    switch(eUserAction)
    {
    case eUserAction_SetSpeed0:
        ExecuteSpeedAction(0);
        break;

    case eUserAction_SetSpeed1:
        ExecuteSpeedAction(1);
        break;
    case eUserAction_SetSpeed2:
        ExecuteSpeedAction(2);
        break;
    case eUserAction_SetSpeed3:
        ExecuteSpeedAction(3);
        break;
    case eUserAction_SetSpeed4:
        ExecuteSpeedAction(4);
        break;
    case eUserAction_SetSpeed5:
        ExecuteSpeedAction(5);
        break;
    case eUserAction_SetSpeed6:
        ExecuteSpeedAction(6);
        break;
    case eUserAction_SetSpeed7:
        ExecuteSpeedAction(7);
        break;
    case eUserAction_SetSpeed8:
        ExecuteSpeedAction(8);
        break;
    case eUserAction_SetSpeed9:
        ExecuteSpeedAction(9);
        break;
    case eUserAction_SetSpeed10:
        ExecuteSpeedAction(10);
        break;
    case eUserAction_SetSpeed11:
        ExecuteSpeedAction(11);
        break;
    case eUserAction_SetSpeed12:
        ExecuteSpeedAction(12);
        break;
    case eUserAction_SetSpeedHalfMax:
        ExecuteSpeedAction((char) (GetMaxShipSpeed() / 2));
        break;
    case eUserAction_SetSpeedMax:
        ExecuteSpeedAction((char) (GetMaxShipSpeed()));
        break;
    case eUserAction_DecreaseSpeed:
        ExecuteSpeedAction((char)GetCurrentShipSpeed() - 1);
        break;
    case eUserAction_IncreaseSpeed:
        ExecuteSpeedAction((char)GetCurrentShipSpeed() + 1);
        break;

    case eUserAction_SetCourse:
        {
            if(mouseHitData.IsDataAvailable())
            {
                struct dir_cpacket packet;
    
                packet.type = (char) EClientPacketType_SetDirection;
                packet.dir = bAngle;
    
                m_pServerConnection->SendPacket((packet_base*) &packet);
            }
        }
        break;

    case eUserAction_Orbit:
        {
            struct orbit_cpacket packet;

            packet.type = (char) EClientPacketType_OrbitPlanetOrStarbase;
            packet.state = 1;

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_LockOn:
        {
            if(mouseHitData.IsDataAvailable())
            {
                if(IsPlayerNearPosition(dwXLocation, dwYLocation))
                {
                    struct playlock_cpacket packet;

                    packet.type = (char) EClientPacketType_LockOnToPlayer;
                    packet.pnum = (char)GetPlayerIDNearPosition(dwXLocation, dwYLocation);

                    m_pServerConnection->SendPacket((packet_base*) &packet);
                }
                else if(IsPlanetNearPosition(dwXLocation, dwYLocation))
                {
                    struct planlock_cpacket packet;

                    packet.type = (char) EClientPacketType_LockOnToPlanet;
                    packet.pnum = (char)GetPlanetIDNearPosition(dwXLocation, dwYLocation);

                    m_pServerConnection->SendPacket((packet_base*) &packet);
                }


            }
        }
        break;

    case eUserAction_Transwarp:
        break;

    case eUserAction_FirePhaser:
        {
            if(mouseHitData.IsDataAvailable())
            {
                struct phaser_cpacket packet;
        
                packet.type = (char) EClientPacketType_FirePhaser;
                packet.dir = bAngle;
        
                m_pServerConnection->SendPacket((packet_base*) &packet);
            }
        }
        break;

    case eUserAction_FirePhoton:
        {
            if(mouseHitData.IsDataAvailable())
            {
                struct torp_cpacket packet;
    
                packet.type = (char) EClientPacketType_FireTorp;
                packet.dir = bAngle;
    
                m_pServerConnection->SendPacket((packet_base*) &packet);
            }
        }
        break;

    case eUserAction_FirePlasma:
        {
            if(mouseHitData.IsDataAvailable())
            {   
                struct plasma_cpacket packet;
    
                packet.type = (char) EClientPacketType_FirePlasma;
                packet.dir = bAngle;
    
                m_pServerConnection->SendPacket((packet_base*) &packet);
            }
        }
        break;

    case eUserAction_DetEnemyTorps:
        {
            struct det_torps_cpacket packet;

            packet.type = (char) EClientPacketType_DetEnemyTorps;

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_DetMyTorps:
        {
            struct det_mytorp_cpacket packet;

            packet.type = (char) EClientPacketType_DetMyTorps;
            // TODO - feed torp number here? Det all torps? Not sure.

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_ToggleShields:
        {        
            struct shield_cpacket packet;

            packet.type = (char) EClientPacketType_ToggleShields;
            packet.state = !(GetLocalPlayerFlags() & ePlayerFlag_Shield);

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_ToggleTractor:
        {        
            bool isTractoring = !!(GetLocalPlayerFlags() & ePlayerFlag_Tract);

            if(isTractoring)
            {
                struct tractor_cpacket packet;

                packet.type = (char) EClientPacketType_ToggleTractor;
                packet.state = !isTractoring;
                packet.pnum = (char) m_pGameWorld->GetPlayerForLocalPlayer()->GetPlayerID();

                m_pServerConnection->SendPacket((packet_base*) &packet);
            }
            else
            {
                if(mouseHitData.IsDataAvailable())
                {
    
                    if(IsPlayerNearPosition(dwXLocation, dwYLocation))
                    {
                        struct tractor_cpacket packet;
    
                        packet.type = (char) EClientPacketType_ToggleTractor;
                        packet.state = !isTractoring;
    
                        packet.pnum = (char) GetPlayerIDNearPosition(dwXLocation, dwYLocation);
                        m_pGameWorld->GetPlayerForLocalPlayer()->SetTractorTarget(packet.pnum);
                        m_pServerConnection->SendPacket((packet_base*) &packet);
                    }
                }
            }

        }
        break;

    case eUserAction_TogglePressor:
        {        
            bool isPressoring = !!(GetLocalPlayerFlags() & ePlayerFlag_Press);

            if(isPressoring)
            {
                struct repress_cpacket packet;

                packet.type = (char) EClientPacketType_TogglePressor;
                packet.state = !isPressoring;
                packet.pnum = (char) m_pGameWorld->GetPlayerForLocalPlayer()->GetPlayerID();

                m_pServerConnection->SendPacket((packet_base*) &packet);
            }
            else
            {
                if(mouseHitData.IsDataAvailable())
                {

                    if(IsPlayerNearPosition(dwXLocation, dwYLocation))
                    {
                        struct repress_cpacket packet;
    
                        packet.type = (char) EClientPacketType_TogglePressor;
                        packet.state = !isPressoring;
    
                        packet.pnum = (char) GetPlayerIDNearPosition(dwXLocation, dwYLocation);
                        m_pGameWorld->GetPlayerForLocalPlayer()->SetTractorTarget(packet.pnum);
                        m_pServerConnection->SendPacket((packet_base*) &packet);
                    }
                }
            }

        }
        break;

    case eUserAction_ToggleCloak:
        {        
            struct cloak_cpacket packet;

            packet.type = (char) EClientPacketType_ToggleCloak;
            packet.state = !(GetLocalPlayerFlags() & ePlayerFlag_Cloaked);

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_EnterRepairMode:
        {        
            struct repair_cpacket packet;

            packet.type = (char) EClientPacketType_EnterRepairMode;
            packet.state = 1;

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_ToggleDockingPermission:
        {
            struct dockperm_cpacket packet;
    
            packet.type = (char) EClientPacketType_SetDockPermission;
            packet.state = !(GetLocalPlayerFlags() & ePlayerFlag_DockOk);
    
            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_BombPlanet:
        {
            struct bomb_cpacket packet;

            packet.type = (char) EClientPacketType_BombPlanet;
            packet.state = !(GetLocalPlayerFlags() & ePlayerFlag_Bomb);

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_BeamDownArmies:
        {
            struct beam_cpacket packet;
         
            packet.type = (char) EClientPacketType_BeamArmies;

            if(!(GetLocalPlayerFlags() & ePlayerFlag_BeamDown))
            {
                packet.state = 2;
            }
            else
            {
                packet.state = 0;
            }

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_BeamUpArmies:
        {
            struct beam_cpacket packet;

            packet.type = (char) EClientPacketType_BeamArmies;
            packet.state = !(GetLocalPlayerFlags() & ePlayerFlag_BeamUp);

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_PracticeRobot:
        {
            struct practr_cpacket packet;

            packet.type = (char) EClientPacketType_CreatePracticeRobot;

            m_pServerConnection->SendPacket((packet_base*) &packet);
        }
        break;

    case eUserAction_GetPlanetDetail:
        {
            if(mouseHitData.IsDataAvailable() && IsPlanetNearPosition(dwXLocation, dwYLocation))
            {
                CPlanetEvent planetEvent(m_pGameWorld->GetPlanetByID(GetPlanetIDNearPosition(dwXLocation, dwYLocation)), eGameWorldEventID_ShowPlanetDetail);
                m_pGameWorld->FireEvent(&planetEvent);
            }
            break;
        }
    }
}

DWORD CUserInput::GetMaxShipSpeed()
{
    
    if(m_pGameWorld->GetPlayerForLocalPlayer()->GetShipData() == NULL)
    {
        return 0;
    }

    return m_pGameWorld->GetPlayerForLocalPlayer()->GetShipData()->GetMaxSpeed();
}

DWORD CUserInput::GetCurrentShipSpeed()
{
    return m_pGameWorld->GetPlayerForLocalPlayer()->GetSpeed();
}

DWORD CUserInput::GetPlayerIDNearPosition(DWORD dwXLocation, DWORD dwYLocation)
{
    DWORD dwUnassigned = (DWORD) -1;
    CPlayerList& playerList = m_pGameWorld->GetPlayerList();
    const unsigned int c_uiShipRadiusSq = c_uiShipRadius * c_uiShipRadius;

    for(CPlayerList::iterator i = playerList.begin(); i != playerList.end(); ++i)
    {
        int dx = (*i)->GetXLocation() - dwXLocation;
        int dy = (*i)->GetYLocation() - dwYLocation;

        if( (dx * dx + dy * dy) < c_uiShipRadiusSq)
        {
            return (*i)->GetPlayerID();
        }
    }


    return dwUnassigned;
}

bool CUserInput::IsPlayerNearPosition(DWORD dwXLocation, DWORD dwYLocation)
{
    DWORD dwUnassigned = (DWORD) -1;

    if(GetPlayerIDNearPosition(dwXLocation, dwYLocation) == dwUnassigned)
    {
        return false;
    }

    return true;
}

DWORD CUserInput::GetPlanetIDNearPosition(DWORD dwXLocation, DWORD dwYLocation)
{
    DWORD dwUnassigned = (DWORD) -1;
    CPlanetList& planetList = m_pGameWorld->GetPlanetList();
    const unsigned int c_uiPlanetRadiusSq = c_uiPlanetRadius * c_uiPlanetRadius;

    for(CPlanetList::iterator i = planetList.begin(); i != planetList.end(); ++i)
    {
        int dx = (*i)->GetXLocation() - dwXLocation;
        int dy = (*i)->GetYLocation() - dwYLocation;

        if( (dx * dx + dy * dy) < c_uiPlanetRadiusSq)
        {
            return (*i)->GetPlanetID();
        }
    }
    return dwUnassigned;
}

bool CUserInput::IsPlanetNearPosition(DWORD dwXLocation, DWORD dwYLocation)
{
    DWORD dwUnassigned = (DWORD) -1;

    if(GetPlanetIDNearPosition(dwXLocation, dwYLocation) == dwUnassigned)
    {
        return false;
    }

    return true;
}

DWORD CUserInput::GetLocalPlayerFlags()
{
    return m_pGameWorld->GetPlayerForLocalPlayer()->GetPlayerFlags();
}

